
`include "common_header.verilog"

//  *************************************************************************
//  File : a_fifo_pcs.v
//  ****************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized Reproduction or Use is Expressly Prohibited. 
//  Copyright (c) 2000 MoreThanIP
//  Designed by Francois Balay
//  fbalay@morethanip.com
//  ****************************************************************************
//  *                    Asynchronous FIFO Optimized for Speed
//  ****************************************************************************
//  Version    : $Id: a_fifo_pcs_xgxs.v,v 1.7 2017/06/07 14:37:54 dk Exp $
//  ****************************************************************************

module a_fifo_pcs_xgxs (

   reset_wclk,
   reset_rclk,
   wclk,
  `ifdef USE_CLK_ENA
   wclk_ena,
  `endif
   wren,
   din,
   rclk,
  `ifdef USE_CLK_ENA
   rclk_ena,
  `endif
   rden,
   dout,
   section,
   sect_e,
   sav,
   septy,
   afull,
   aempty);

parameter FF_WIDTH     = 72;
parameter DEPTH        = 16;
parameter ADDR_WIDTH   = 4;
parameter AF_THRESHOLD = 4'd 12;        // NOTE: changed to an absolute value, not (DEPTH - AF_THRESHOLD - 1);
parameter AE_THRESHOLD = 4'd 1;

input   reset_wclk;                     //  Reset - wclk Domain 
input   reset_rclk;                     //  Reset - rclk Domain 
input   wclk;                           //  Write Clock
input   wren;                           //  Write Enable
input   [FF_WIDTH - 1:0] din;           //  Write Data
input   rclk;                           //  Read Clock
input   rden;                           //  Read Enable
output  [FF_WIDTH - 1:0] dout;          //  Read Data
input   [ADDR_WIDTH - 1:0] section;     //  Section Threshold used to generate SAV (read clock domain)
input   [ADDR_WIDTH - 1:0] sect_e;      //  Section Threshold used to generate SEPTY (write clock domain)
output  sav;                            //  Packet Section Available
output  septy;                          //  Packet Section Empty                
output  afull;                          //  Set to '1' when FIFO is Almost Full
output  aempty;                         //  Set to '1' when FIFO is Almost Empty

`ifdef USE_CLK_ENA
input   wclk_ena;
input   rclk_ena;
`endif

//  Write Port Interface
//  --------------------

wire    [FF_WIDTH - 1:0] dout; 
wire    sav; 
wire    septy; 
wire    afull; 
wire    aempty; 
wire    [ADDR_WIDTH - 1:0] wr_b_ptr; 
wire    [ADDR_WIDTH - 1:0] wr_g_ptr; 
wire    [ADDR_WIDTH - 1:0] rd_g_wptr; 
wire    [ADDR_WIDTH - 1:0] ptr_wck_diff; 

//  Read Clock Domain Signals
//  -------------------------

wire    [ADDR_WIDTH - 1:0] rd_b_ptr; 
wire    [ADDR_WIDTH - 1:0] rd_g_ptr; 
wire    [ADDR_WIDTH - 1:0] wr_g_rptr; 
wire    [ADDR_WIDTH - 1:0] ptr_rck_diff; 

//  Inter-Clock Domains Flags
//  -------------------------

reg     afull_flag; 
reg     aempty_flag; 
reg     sav_flag; 
reg     septy_flag; 

//  Local Variables
//  ---------------
wire    [ADDR_WIDTH - 1:0]  FF_RD_BINVAL; 
wire    [ADDR_WIDTH - 1:0]  FF_WR_BINVAL; 
wire    [ADDR_WIDTH - 1:0]  af_threshold_cmp; 
wire    [ADDR_WIDTH - 1:0]  ae_threshold_cmp; 

reg     [ADDR_WIDTH - 1:0]  ff_wr_binval_reg;   // for aempty qualification
reg     ff_wr_binval_eq;                        // binval and binval_reg were equal in previous clock


// convert integers into vectors for strict type
// ---------------------------------------------

assign af_threshold_cmp = AF_THRESHOLD; // (DEPTH - AF_THRESHOLD - 1);
assign ae_threshold_cmp = AE_THRESHOLD;

//  Synchronous Dual Port Memory - Synchronous Write / Read
//  -------------------------------------------------------

sdpm_rm_gen #(FF_WIDTH,DEPTH,ADDR_WIDTH) U_RAM_RM (

           `ifdef FF_USE_REGS

           .reset_wclk(reset_wclk),
           .reset_rclk(reset_rclk),

           `endif

           .wad(wr_b_ptr),
           .din(din),
           .wclk(wclk),
           .rclk(rclk),
           .wren(wren),
           .rad(rd_b_ptr),
           .dout(dout));

gray_cnt_xgxs #(ADDR_WIDTH,DEPTH) U_WRCNT (

          .clk(wclk),
        `ifdef USE_CLK_ENA
          .clk_ena(wclk_ena),
        `endif           
          .reset(reset_wclk),
          .sync_rst(1'b0),
          .enable(wren),
          .b_out(wr_b_ptr),
          .g_out(wr_g_ptr));

gray_cnt_xgxs #(ADDR_WIDTH,DEPTH) U_RDCNT (

          .clk(rclk),
        `ifdef USE_CLK_ENA
          .clk_ena(rclk_ena),
        `endif           
          .reset(reset_rclk),
          .sync_rst(1'b0),
          .enable(rden),
          .b_out(rd_b_ptr),
          .g_out(rd_g_ptr));

mtip_xsync #(ADDR_WIDTH) U_SYRDGPTR (
        .data_in(rd_g_ptr),
        .reset  (reset_wclk),
        .clk    (wclk),
        .data_s (rd_g_wptr));


always @(posedge wclk or posedge reset_wclk)
   begin : ff_rd
   if (reset_wclk == 1'b 1)
      begin
      afull_flag    <= 1'b 0;	
      septy_flag    <= 1'b 1;	
      end
   else
      begin
      
         `ifdef USE_CLK_ENA
            if(wclk_ena == 1'b 1)
            begin
         `endif      
      
              
                
                // Almost Full
                // -----------
                
              if ((ptr_wck_diff) > af_threshold_cmp)
                 begin
                 afull_flag <= 1'b 1;	
                 end
              else
                 begin
                 afull_flag <= 1'b 0;	
                 end
        
                //  Free Packet Section Available Flag Generation
                //  ---------------------------------------------
        
              if ((sect_e != 0) & (ptr_wck_diff > sect_e) )
                 begin
                 septy_flag <= 1'b 0;	
                 end
              else
                 begin
                 septy_flag <= 1'b 1;	
                 end

         `ifdef USE_CLK_ENA
            end
         `endif      
      
      end
   end
   
assign FF_RD_BINVAL[ADDR_WIDTH-1]   = rd_g_wptr[ADDR_WIDTH-1] ;
assign FF_RD_BINVAL[ADDR_WIDTH-2:0] = gray2bin(rd_g_wptr) ;

assign ptr_wck_diff = wr_b_ptr - FF_RD_BINVAL;	        // implicit modulo



assign afull = afull_flag; 

assign septy = septy_flag; 

// -----------------
// Read Clock Domain
// -----------------

mtip_xsync #(ADDR_WIDTH) U_SYWRGRPTR (
        .data_in(wr_g_ptr),
        .reset  (reset_rclk),
        .clk    (rclk),
        .data_s (wr_g_rptr));


always @(posedge rclk or posedge reset_rclk)
   begin : ff_wr
   if (reset_rclk == 1'b 1)
      begin
      aempty_flag   <= 1'b 1;	
      sav_flag      <= 1'b 0;	
      ff_wr_binval_reg <= {ADDR_WIDTH{1'b 0}};
      ff_wr_binval_eq  <= 1'b 0;
      end
   else
      begin

         `ifdef USE_CLK_ENA
            if(rclk_ena == 1'b 1)
            begin
         `endif            
              
                // create almost empty flag
                // ------------------------
                // we qualify it, that it is true only, if the write pointer is unmodified for 3 consecutive writes
                // indicating that the write side has stopped writing
                                
              ff_wr_binval_reg <= FF_WR_BINVAL;  
              if( ff_wr_binval_reg == FF_WR_BINVAL )
                begin
                
                        ff_wr_binval_eq <= 1'b 1;
                
                end
                else
                begin
                        
                        ff_wr_binval_eq <= 1'b 0;

                end              
              
        
              if (((ptr_rck_diff < ae_threshold_cmp) & (FF_WR_BINVAL==ff_wr_binval_reg) & (ff_wr_binval_eq==1'b 1)) |   // write side has stopped writing
                  ( ((ptr_rck_diff == 4'h f) | (ptr_rck_diff == 4'h e)) &                        // underflow
                    ((FF_WR_BINVAL!=ff_wr_binval_reg) | (ff_wr_binval_eq==1'b 0))) )        // while writing
                 begin
                 aempty_flag <= 1'b 1;	
                 end
              else
                 begin
                 aempty_flag <= 1'b 0;	
                 end
        
                // Section Available
                // -----------------
                
              if (((section != 0) & (ptr_rck_diff < section )) | (ptr_rck_diff > 4'h d)) // e,f are empty conditions
                 begin
                 sav_flag <= 1'b 0;	
                 end
              else
                 begin
                 sav_flag <= 1'b 1;	
                 end
        
         `ifdef USE_CLK_ENA
            end
         `endif
      
      end

   end
   
assign FF_WR_BINVAL[ADDR_WIDTH-1]   = wr_g_rptr[ADDR_WIDTH-1] ;
assign FF_WR_BINVAL[ADDR_WIDTH-2:0] = gray2bin(wr_g_rptr) ;

assign ptr_rck_diff = FF_WR_BINVAL - rd_b_ptr;	

// assert empty when the write side has stopped writing and read=write.
// We check "write side has stopped" by checking equalness of 3 consecutive write pointer values.
// Suppress single clock cycle assertion due to temporary grey-count propagation delay.
assign aempty = (aempty_flag==1'b 1) | 
                ((FF_WR_BINVAL==ff_wr_binval_reg) & (ff_wr_binval_eq==1'b 1) & (ff_wr_binval_reg==rd_b_ptr)) ? 1'b 1 : 1'b 0; 

assign sav = sav_flag;

// Gray to Binary Conversion
// -------------------------

function [ADDR_WIDTH-2:0] gray2bin;

        input [ADDR_WIDTH-1:0]  gray_val ;
        
        integer LOOP_IDX1; 
        integer LOOP_IDX2;
                
        for (LOOP_IDX1 = 0; LOOP_IDX1 <= ADDR_WIDTH - 2; LOOP_IDX1 = LOOP_IDX1 + 1)
        begin
         
                gray2bin[LOOP_IDX1] = gray_val[LOOP_IDX1];	

                for (LOOP_IDX2 = ADDR_WIDTH - 1; LOOP_IDX2 >= LOOP_IDX1 + 1; LOOP_IDX2 = LOOP_IDX2 - 1)
                begin
            
                        gray2bin[LOOP_IDX1] = gray2bin[LOOP_IDX1] ^ gray_val[LOOP_IDX2];	
            
                end
                
        end
        
endfunction

endmodule // module a_fifo_pcs